#include <stdlib.h>
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/pgmspace.h>

#include "uart.h"
#include "usbn2mc.h"
#define F_CPU 16000000U
#include <util/delay.h>

#include "../usbn2mc/main/usbnapi.h"
#include "usbn2mc.h"

//#include "avrlib/timer128.h"

#include "main.h"
#include "common.h"
#include "protocol.h"

#include "io.h"
#include "adc.h"
#include "i2c.h"
#include "spi.h"
#ifdef O_STARCARD
#include "ramcard.h"
#endif

//////////////////////////////////////////////////////////////////////////////

void delay_250ms (void)
{
	for (uint8_t i = 0; i < 25; i++)
		_delay_ms (10);			// max delay is 262ms / 16 MHz = 16ms
}

//////////////////////////////////////////////////////////////////////////////

SIGNAL (SIG_INTERRUPT4)
{
	USBNInterrupt ();
}

//SIGNAL (SIG_UART0_RECV)
//{
//}

//////////////////////////////////////////////////////////////////////////////
// internal timer

SIGNAL (SIG_OVERFLOW0)
{
	//PORTB = 0xFF;
	//PORTB = 0x00;

	common_scheduler ();

	TCNT0 = octopus.latency_timer;
}

//////////////////////////////////////////////////////////////////////////////

/* called after data where send to pc */
static void USBMessageSent (void)
{
	//PORTD ^= (1 << PD7);
	//CommandAnswer(2);
}

///* called after data where send to pc */
//static void USBAskStatus (void)
//{
//}
//
///* called after data where send to pc */
//static void USBGetLog (void)
//{
//}

/* is called when received data from pc */
static void USBMessageIn (uint8_t *buf)
{
	uint8_t check = (buf[0] >> 4) & 0x0F;

	switch (check)
	{
	case 0:
		common_parser (buf);
		break;
	case 1:
		io_parser (buf);
		break;
	case 2:
		adc_parser (buf);
		break;
	case 3:
		i2c_parser (buf);
		break;
	case 4:
		spi_parser (buf);
		break;
#ifdef O_STARCARD
	case 15:
		rc_parser (buf);
		break;
#endif

	default:
		answer[0] = buf[0];
		answer[1] = RSP_UNKOWN_CMD;
		answer[2] = 0;
		CommandAnswer (3);
	}
}

//////////////////////////////////////////////////////////////////////////////

void CommandAnswer (int length)
{
//	int     i;
//
//	USBNWrite (FIFOTXC1, FLUSH);
//	for (i = 0; i < length; i++)
//		USBNWrite (TXD1, answer[i]);
//
//	/* control togl bit */
//	if (octopus.togl == 1)
//	{
//		USBNWrite (FIFOTXC1, TX_LAST + TX_EN + TX_TOGL);
//		octopus.togl = 0;
//	}
//	else
//	{
//		USBNWrite (FIFOTXC1, TX_LAST + TX_EN);
//		octopus.togl = 1;
//	}

	USBNTransmitFIFO1 (length, answer);
}

//////////////////////////////////////////////////////////////////////////////

static void	SetupDescriptorTree (void)
{
	// setup usb descriptor tree
	USBNInit ();					// build basic device descriptor
	USBNDeviceVendorID  (0x1781);	// 0x0400 is the number from national
	USBNDeviceProductID (0x0c65);	// add your product id
	USBNDeviceBCDDevice (0x0001);	// you can use it as version e.g. version 1.02 

//	char    lang[] = { 0x09, 0x04, 0x00 };	// was UNUSED
//	_USBNAddStringDescriptor (lang);	// index 0 in strings language descriptor

	// Achtung!!! Deskriptor muessen immer 8*x gross sein! (Fehler im Stack) (KH: where ?)
	USBNDeviceManufacture_P (PSTR("EmbeddedProjects"));
	USBNDeviceProduct_P (PSTR("OctopusUSB Interface Converter and I/O Extension"));
	USBNDeviceSerialNumber_P (PSTR("20081108"));

	conf_t *pconf = USBNAddConfiguration ();
	USBNConfigurationPower (pconf, 100);

	interf_t *pinterf = USBNAddInterface (pconf, 0);
	USBNAlternateSetting (pconf, pinterf, 0);

	/* communication */
	// EP = 0x81 = to   host PC (IN)
	// EP = 0x01 = from host PC (OUT)
	USBNAddInEndpoint  (pconf, pinterf, 1, 0x01, BULK, 64, 0, USBMessageSent);
	USBNAddOutEndpoint (pconf, pinterf, 1, 0x01, BULK, 64, 0, &USBMessageIn);	// 0x01

//	/* state */
//	USBNAddInEndpoint (pconf, pinterf, 1, 0x02, INTR, 64, 1, USBAskStatus);
//	/* logging */
//	USBNAddInEndpoint (pconf, pinterf, 1, 0x03, BULK, 64, 1, USBGetLog);

	USBNCreateConfDescrField ();
}

//////////////////////////////////////////////////////////////////////////////

int main (void)
{
//static void    (*jump_to_bootloader) (void) = (txfunc_t *) 0xF000;
//	jump_to_bootloader ();	// Jump to application sector

	UARTInit ();
	
	SetupDescriptorTree ();

	// initialize start the usb hardware
	USBNInitMC ();		// has a enable interupt sei()

	// start usb chip
	USBNStart ();

	// initialize the timer system
	common_latency_timer_init ();

	io_init ();
#ifdef O_STARCARD
	rc_init ();
#endif

	//DDRD = 0xFF;
	//PORTD = 0x00;
	//PORTD = 0xff;

	DDRB  |= (1 << DDB7);

	// hello world led pattern
	DDRC = 0xFF;
	// hello world led pattern
	PORTC = 0xAA;
	delay_250ms ();
	PORTC = 0xD5;
	delay_250ms ();
	PORTC = 0x80;				// keep USBN /CS high

	MCUCR = (MCUCR & ~0b00011100) | (0b000 << 2);
	MCUCR |= (1 << SE);
	while (1)
	{
#if 0
		// use this only for debug
		// otherwise the PC may want to drive the status LED
		STATUS_LED_on;		// flash status every time we wake up 
		_delay_ms (300);
		STATUS_LED_off;
#endif
		asm("sleep");
	}

	return 0;
}
